package com.lunker.moneyrecord;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import android.app.Activity;
import android.app.Dialog;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.view.ViewConfiguration;
import android.webkit.WebView;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import android.widget.TextView;
import android.widget.Toast;

public class Summary extends Activity implements OnClickListener, OnTouchListener {
	
	private Calendar displayDate = Calendar.getInstance();
	private Dialog aboutDialog = null;
	private SimpleCursorAdapter adapter = null;
	private final String listDataKey[] = new String[] {"rid", "day", "cname", "amount"};
	private int listDataTo[] = null;
	private float lastX;
	private float lastY;
	private static final int MY_ACTION_NONE = 0;
	private static final int MY_ACTION_FLING = 1;
	private int myAction = MY_ACTION_NONE;
	private float MIN_TOUCH_SLOP;
	private static final int REQUEST_CHECK_PASSWORD = 0;
	private static final int REQUEST_CHANGE_SETTINGS = 1;
	public static final int REQUEST_NEW_RECORD = 2;
	private SharedPreferences config = null;
	private SQLiteDatabase mydb = null;
	private NotificationManager nmgr = null;
	
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.summary);
        registerForContextMenu(findViewById(R.id.listview_records));
        
        config = getSharedPreferences("Settings", Context.MODE_PRIVATE);
        mydb = new MyDatabaseOpenHelper(this).getWritableDatabase();
        nmgr = (NotificationManager)this.getSystemService(Context.NOTIFICATION_SERVICE);
        MIN_TOUCH_SLOP = ViewConfiguration.get(this).getScaledTouchSlop();
        listDataTo = new int[] {
        		R.id.textview_recordid,
        		R.id.textview_record_date,
        		R.id.txt_record_catalog,
        		R.id.textview_record_amount
        };
        
        ((Button) findViewById(R.id.btn_nextMonth)).setOnClickListener(this);
        ((Button) findViewById(R.id.btn_prevMonth)).setOnClickListener(this);
        ((Button) findViewById(R.id.btn_addRecord)).setOnClickListener(this);
        ((TextView) findViewById(R.id.text_thisMonth)).setOnClickListener(this);        
        ((ListView)findViewById(R.id.listview_records)).setOnTouchListener(this);
        		
		//Check password while this activity is created.
		boolean usePassword = config.getBoolean(Integer.toString(Settings.TAG_USE_PASSWORD), false);
		String password = config.getString("password", null);
		
		if (usePassword && password != null) {
			Intent intent = new Intent(this, com.lunker.moneyrecord.Lock.class);
			this.startActivityForResult(intent, REQUEST_CHECK_PASSWORD);
		}
    }

	@Override
	protected void onStart() {		
		super.onStart();
		 //Reset display date to today every time re-opening this activity. 
		displayDate = Calendar.getInstance();
		updateDisplayDate();
		updateSummery();
	}

	@Override
	protected void onDestroy() {
		nmgr.cancelAll();
		mydb.close();
		super.onDestroy();
	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		switch (requestCode) {
		case REQUEST_CHECK_PASSWORD:
			if (resultCode != Lock.RESULT_PASSWORD_IS_CORRECT) {
				//Return abnormally from Lock activity. Don't let user in.
				finish();
			} else {
				PendingIntent pintent = PendingIntent.getActivity(this, 0, getIntent(), 0);
				Notification notification = new Notification(
						R.drawable.icon, 
						getString(R.string.unlocked), 
						System.currentTimeMillis());
				notification.flags |= Notification.FLAG_NO_CLEAR;
				notification.setLatestEventInfo(this, getString(R.string.app_name), 
						getString(R.string.click_here_to_open), pintent);
				nmgr.notify(0, notification);
			}
			break;
		case REQUEST_CHANGE_SETTINGS:
			//Reload the setting of lock.
			if (config.getBoolean(Integer.toString(Settings.TAG_USE_PASSWORD), false)) {
				PendingIntent pintent = PendingIntent.getActivity(this, 0, this.getIntent(), 0);
				Notification notification = new Notification(
						R.drawable.icon, 
						getString(R.string.unlocked), 
						System.currentTimeMillis());
				notification.flags |= Notification.FLAG_NO_CLEAR;
				notification.setLatestEventInfo(this, getString(R.string.app_name), 
						getString(R.string.click_here_to_open), pintent);
				nmgr.notify(0, notification);
			} else {
				nmgr.cancelAll();
			}
			break;
		case REQUEST_NEW_RECORD:
			if (resultCode == NewRecordActivity.RESULT_YES) {
				updateSummery();
			}
			break;
		}
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		new MenuInflater(this).inflate(R.menu.summary_menu, menu);
		return true;
	}

	@Override
	public boolean onPrepareOptionsMenu(Menu menu) {
		menu.findItem(R.id.menu_lock).setEnabled(
				config.getBoolean(Integer.toString(Settings.TAG_USE_PASSWORD), false));
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case R.id.menu_settings:
			startActivityForResult(new Intent(this, com.lunker.moneyrecord.Settings.class),
					REQUEST_CHANGE_SETTINGS);
			break;
		case R.id.menu_about:
			openAboutDialog();
			break;
		case R.id.menu_lock:
			//Unlocked state is cleaned in onDestroy();
			finish();
			break;
		}
		
		return true;
	}

	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.btn_nextMonth:
			displayDate.add(Calendar.MONTH, 1);
			updateDisplayDate();
			updateSummery();
			break;
		case R.id.btn_prevMonth:
			displayDate.add(Calendar.MONTH, -1);
			updateDisplayDate();
			updateSummery();
			break;
		case R.id.text_thisMonth:
			displayDate = Calendar.getInstance();
			updateDisplayDate();
			updateSummery();
			Toast.makeText(this, getText(R.string.returned_to_this_month), 
					Toast.LENGTH_SHORT).show();
			break;
		case R.id.btn_about_dismiss:
			aboutDialog.dismiss();
			break;
		case R.id.btn_addRecord:
			Intent intent = new Intent(this, com.lunker.moneyrecord.NewRecordActivity.class);
			startActivityForResult(intent, REQUEST_NEW_RECORD);
			break;
		}
	}
	
	private void updateDisplayDate () {
		((TextView) findViewById(R.id.text_thisMonth)).setText(
				new SimpleDateFormat("yyyy - MM").format(displayDate.getTime()));
	}
		
	private void updateSummery () {
		String year = Integer.toString(displayDate.get(Calendar.YEAR));
		String month = Integer.toString(displayDate.get(Calendar.MONTH));
		Cursor cr = null;
		
		//Query sum
		cr = mydb.query(
				"money_record", 
				new String[] {
						"total(amount)"
				}, 
				"year = ? and month = ?", 
				new String[] {
					year,
					month
				}, 
				null, 
				null,
				null);
		startManagingCursor(cr);
		cr.moveToFirst();
		((TextView) findViewById(R.id.text_total_value)).setText(Float.toString(cr.getFloat(0)));
				
		cr = mydb.query(
				"money_record left join catalog on cid = catalog", 
				new String[] {
						"rid as _id", 
						"rid", 
						"day",
						"amount",
						"cname"},
				"year = ? and month = ?", 
				new String[] {
						year, 
						month}, 
				null, 
				null, 
				"day desc, rid desc");
		startManagingCursor(cr);
		adapter = new SimpleCursorAdapter(
				this,
				R.layout.record_item,
				cr,
				listDataKey,
				listDataTo);
		((ListView) findViewById(R.id.listview_records)).setAdapter(adapter);
	}

	@Override
	public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {		
		new MenuInflater(this).inflate(R.menu.record_item_context_menu, menu);
	}	

	@Override
	public boolean onContextItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case R.id.cmenu_del:
			AdapterView.AdapterContextMenuInfo info = 
				(AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
			//The TextView where we store the real row ID in database.
			TextView v = (TextView) info.targetView.findViewById(R.id.textview_recordid);
			mydb.execSQL("DELETE FROM money_record WHERE rid = " + v.getText() + ";");
			updateSummery();
			updateWidget();
			Toast.makeText(this, getString(R.string.one_record_is_deleted), 
					Toast.LENGTH_SHORT).show();
			break;
		}
		
		return true;
	}

	private void openAboutDialog () {
		if (aboutDialog == null) {
			aboutDialog = new Dialog(this);
			aboutDialog.setContentView(R.layout.about_dialog);
			aboutDialog.setTitle(R.string.about);
			((WebView) aboutDialog.findViewById(R.id.webview_about)).loadUrl(
					"file:///android_asset/about.html");
			((Button) aboutDialog.findViewById(R.id.btn_about_dismiss)).setOnClickListener(this);
		}
		
		aboutDialog.show();
	}

	@Override
	public boolean onTouch(View v, MotionEvent ev) {
		//Get fling gesture on ListView.
		/*
		 * Touch events in Android go onTouch() of an onTouchListener first then the view's 
		 * onTouchEvent(). Returning true from onTouch() makes touch events not continuing 
		 * going to onTouchEvent(). Otherwise, it does. So we can find and consume horizontal 
		 * fling gesture by returning true from onTouch(), then pass others we don't want to 
		 * ListView's onTouchEvent() by returning false from onTouch().   
		 */
		int act = ev.getAction();
		
		if (myAction == MY_ACTION_FLING && act == MotionEvent.ACTION_MOVE) {
			//Ignore move events while flinging.
			return true; //Don't pass this event to ListView's onTouchEvent();
		}
		
		switch (act) {
		case MotionEvent.ACTION_MOVE:
				float diffX = this.lastX - ev.getX();
				float diffY = this.lastY - ev.getY();
				
				if (Math.abs(diffX) < MIN_TOUCH_SLOP || Math.abs(diffY) >= MIN_TOUCH_SLOP) {
					//Not the fling gesture we want, pass it to ListView's onTouchEvent().
					return false;
				} else {
					myAction = MY_ACTION_FLING;
				}
				
				if (diffX > 0) {
					displayDate.add(Calendar.MONTH, 1);
				} else if (diffX < 0){
					displayDate.add(Calendar.MONTH, -1);
				}
				
				updateDisplayDate();
				updateSummery();
				
				/*
				 * We have found a fling gesture.
				 * Fire a cancel action to ListView to cancel item selection event.
				 * (Because it looks better in this way. Orz.  
				 */
				MotionEvent cancelEvent = MotionEvent.obtain(ev);
				cancelEvent.setAction(MotionEvent.ACTION_CANCEL);
				v.dispatchTouchEvent(cancelEvent);
				return true;
		case MotionEvent.ACTION_DOWN:
			this.lastX = ev.getX();
			this.lastY = ev.getY();
		case MotionEvent.ACTION_UP:
			this.myAction = MY_ACTION_NONE;
		default:
			return false;
		}
	}
	
	private void updateWidget () {
		new WidgetProvider().notifyChange(this);
	}
}